/****************************************************************************
 *  _____       ______  _____
 * |_   _|     |  ____|/ ____|
 *   | |  _ __ | |__  | (___    Institute of Embedded Systems
 *   | | | '_ \|  __|  \___ \   Zürcher Hochschule für Angewandte
 *  _| |_| | | | |____ ____) |  Wissenschaften
 * |_____|_| |_|______|_____/   8401 Winterthur, Switzerland
 *
 ****************************************************************************
 *
 * @version $Rev: 212 $
 * @author  $Author: bacr $
 * @date    $Date: 2014-01-10 14:09:50 +0100 (ven., 10 janv. 2014) $
 *
 ***************************************************************************/

#ifndef __PTP2_LOG_H__
#define __PTP2_LOG_H__

#include <stdint.h>
#include <stdarg.h>

#include "hcpp_config.h"
#include "hcpp/ptp2_types.h"
#include "packet/packet.h"

/* ptp2_log_bit_set */
#include "hal/avalon_hal.h"
#include "hal/register.h"

enum {
    PTP2_LOG_ERROR      = 0,
    PTP2_LOG_WARNING    = 1,
    PTP2_LOG_INFO       = 2,
    PTP2_LOG_DEBUG          = 3,
    PTP2_LOG_DEBUG_VERBOSE  = 4
};

enum {
    PTP2_LOG_MASTER,
    PTP2_LOG_PORT,
    PTP2_LOG_TASK,
    PTP2_LOG_TASK_QUEUE,
    PTP2_LOG_PACKET,
    PTP2_LOG_PACKET_ANALYZER,
    PTP2_LOG_PACKET_ASSEMBLER,
    PTP2_LOG_NODE_TABLE,
    PTP2_LOG_SHADOW_TABLE,
    PTP2_LOG_BMC,
    PTP2_LOG_HAL,
    PTP2_LOG_SPI_MASTER,
    PTP2_LOG_COUNT
};

#define PTP2_LOG_MAC_LEN                    17 + 1    /* MAC-address, with NUL-character */
#define PTP2_LOG_IPV4_LEN                   15 + 1    /* IPv4-address, with NUL-character */
#define PTP2_LOG_IPV6_LEN                   41 + 1    /* IPv6-address, with NUL-character */
#define PTP2_LOG_CLOCK_IDENTITY_LEN         23 + 1    /* clock identity, with NUL-character */

#if PTP2_LOG
#define PTP2_LOG_FUNCTION(with_header, function, category, level, msg) \
    do { \
        if ((category == PTP2_LOG_MASTER            && level <= PTP2_LOG_LEVEL_MASTER)           || \
            (category == PTP2_LOG_PORT              && level <= PTP2_LOG_LEVEL_PORT)             || \
            (category == PTP2_LOG_TASK              && level <= PTP2_LOG_LEVEL_TASK)             || \
            (category == PTP2_LOG_TASK_QUEUE        && level <= PTP2_LOG_LEVEL_TASK_QUEUE)       || \
            (category == PTP2_LOG_PACKET            && level <= PTP2_LOG_LEVEL_PACKET)           || \
            (category == PTP2_LOG_PACKET_ANALYZER   && level <= PTP2_LOG_LEVEL_PACKET_ANALYZER)  || \
            (category == PTP2_LOG_PACKET_ASSEMBLER  && level <= PTP2_LOG_LEVEL_PACKET_ASSEMBLER) || \
            (category == PTP2_LOG_NODE_TABLE        && level <= PTP2_LOG_LEVEL_NODE_TABLE)       || \
            (category == PTP2_LOG_SHADOW_TABLE      && level <= PTP2_LOG_LEVEL_SHADOW_TABLE)     || \
            (category == PTP2_LOG_BMC               && level <= PTP2_LOG_LEVEL_BMC)              || \
            (category == PTP2_LOG_HAL               && level <= PTP2_LOG_LEVEL_HAL)) { \
            if (with_header) { \
                ptp2_log_print_header(category, level); \
            } \
            function msg; \
        } \
    } while(0)

#define PTP2_LOG_PRINT(category, level, msg)    PTP2_LOG_FUNCTION(1, ptp2_log_print, category, level, msg)
#define PTP2_LOG_PRINTLN(category, level, msg)  PTP2_LOG_FUNCTION(1, ptp2_log_println, category, level, msg)
#define PTP2_LOG_APPEND(category, level, msg)   PTP2_LOG_FUNCTION(0, ptp2_log_append, category, level, msg)
#define PTP2_LOG_APPENDLN(category, level, msg) PTP2_LOG_FUNCTION(0, ptp2_log_appendln, category, level, msg)
#define PTP2_LOG_RAW_PACKET(category, level, raw_packet)    PTP2_LOG_FUNCTION(1, ptp2_log_raw_packet, category, level, raw_packet)

/* converts a defined type into a string */
#define PTP2_LOG_MAC(var, var_str)              uint8_t var_str[PTP2_LOG_MAC_LEN];              ptp2_log_mac(var, var_str)
#define PTP2_LOG_IPV4(var, var_str)             uint8_t var_str[PTP2_LOG_IPV4_LEN];             ptp2_log_ipv4(var, var_str)
#define PTP2_LOG_IPV6(var, var_str)             uint8_t var_str[PTP2_LOG_IPV6_LEN];             ptp2_log_ipv6(var, var_str)
#define PTP2_LOG_CLOCK_IDENTITY(var, var_str)   uint8_t var_str[PTP2_LOG_CLOCK_IDENTITY_LEN];   ptp2_log_clock_identity(var, var_str)

#else /* PTP2_LOG */

#define PTP2_LOG_PRINT(category, level, msg)
#define PTP2_LOG_PRINTLN(category, level, msg)
#define PTP2_LOG_APPEND(category, level, msg)
#define PTP2_LOG_APPENDLN(category, level, msg)

#define PTP2_LOG_MAC(var, var_str)
#define PTP2_LOG_IPV4(var, var_str)
#define PTP2_LOG_IPV6(var, var_str)
#define PTP2_LOG_CLOCK_IDENTITY(var, var_str)

#define PTP2_LOG_RAW_PACKET(category, level, raw_packet)

#endif /* PTP2_LOG */

void ptp2_log_init();

void ptp2_log_print_header(uint16_t category, uint16_t level);
void ptp2_log_print(const char *format, ...)  __attribute__ ((format (printf, 1, 2)));
void ptp2_log_println(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void ptp2_log_append(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void ptp2_log_appendln(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void ptp2_log_raw_packet(raw_packet_t *raw_packet);

void ptp2_log_mac(const mac_address_t *mac, uint8_t *str);
void ptp2_log_ipv4(const ipv4_address_t *ipv4, uint8_t *str);
void ptp2_log_ipv6(const ipv6_address_t *ipv6, uint8_t *str);
void ptp2_log_clock_identity(const ptp2_clock_identity_t *clock_identity, uint8_t *str);

static inline void ptp2_log_bit_set(uint8_t bit)
{
#ifdef __NIOS2__
#if USE_AVALON_REGISTER_USER0
    volatile uint32_t value = av_bus_read_32(HOST_REG_USER0);
    av_bus_write_32(HOST_REG_USER0, value | (1 << bit));
#endif
#endif
}

static inline void ptp2_log_bit_clear(uint8_t bit)
{
#ifdef __NIOS2__
#if USE_AVALON_REGISTER_USER0
    volatile uint32_t value = av_bus_read_32(HOST_REG_USER0);
    av_bus_write_32(HOST_REG_USER0, value & ~(1 << bit));
#endif
#endif
}

const char *ptp2_log_packet_type(packet_type_t packet_type);
const char *ptp2_log_message_type(uint8_t msg_type);
const char *ptp2_log_tlv_type(uint16_t tlv_type);
const char *ptp2_log_port_state(ptp2_port_state_t port_state);

#endif


